#ifndef __BASIC_SAFEQUEUE_H__
#define __BASIC_SAFEQUEUE_H__

#include <atomic>
#include <mutex>

#ifndef NS_START_THREAD_SAFE
#define NS_START_THREAD_SAFE namespace __thread_safe__ {
#endif

#ifndef NS_END
#define NS_END }
#endif

#ifndef NS_USE_THREAD_SAFE
#define NS_USE_THREAD_SAFE using namespace __thread_safe__;
#endif

NS_START_THREAD_SAFE

template <typename T>
class tpl_queue final {
  struct _Node final {
    _Node(T* _d) : data(_d) {
    }
    _Node() {
    }
    ~_Node() {
      next = nullptr;
      data = nullptr;
    }
    _Node(const _Node&) = delete;
    _Node* operator=(const _Node&) = delete;
    T* data = nullptr;
    _Node* next = nullptr;
  };

  std::mutex m_mutex;
  _Node m_root;
  std::atomic_int m_size;
  _Node* m_pHead;

public:
  tpl_queue() : m_size(0), m_pHead(&m_root) {
  }
  ~tpl_queue() {
    Clear();
  }
  void Push(T* d) noexcept {
    std::lock_guard<std::mutex> _locker(m_mutex);
    _Node* _tmp = new _Node(d);
    m_pHead->next = _tmp;
    m_pHead = _tmp;
    ++m_size;
  }

  T* Pop() noexcept {
    std::lock_guard<std::mutex> _locker(m_mutex);
    if (m_size <= 0) {
      return nullptr;
    }
    _Node* _tmp = m_root.next;
    m_root.next = _tmp->next;
    if (_tmp == m_pHead)
      m_pHead = &m_root;
    T* data = _tmp->data;
    delete _tmp;
    --m_size;
    return data;
  }

  /**
   * return true if successfully removed
   */
  bool Remove(T* d) noexcept {
    if (!d)
      return false;
    std::lock_guard<std::mutex> _locker(m_mutex);
    if (m_size <= 0) {
      return false;
    }
    bool _b{false};
    _Node* _preNode{&m_root};
    _Node* _targetNode{m_root.next};
    while (_targetNode) {
      if (_targetNode->data == d) {
        _b = true;
        break;
      }
      _preNode = _targetNode;
      _targetNode = _targetNode->next;
    }
    if (!_b)
      return false;

    _preNode->next = _targetNode->next;
    if (_targetNode == m_pHead)
      m_pHead = _preNode;
    delete _targetNode;
    --m_size;
    return true;
  }

  bool IsEmpty() const noexcept {
    return m_size <= 0;
  }
  int GetSize() const noexcept {
    return (int)m_size;
  }
  void Clear() noexcept {
    std::lock_guard<std::mutex> _locker(m_mutex);
    while (m_size > 0) {
      _Node* _tmp = m_root.next;
      m_root.next = _tmp->next;
      delete _tmp;
      --m_size;
    }
  }
};

NS_END
#endif
